from flask import Flask, g
from flask import abort
from flask import request
from flask import Blueprint, render_template
from flask import Blueprint, current_app
import types
from database import *
import os
import time
import stat
import hashlib
import json
import config
import re
from werkzeug.wrappers import BaseResponse as Response


def get_real_addr():
    if 'HTTP_X_REAL_IP' in request.environ:
        return request.environ['HTTP_X_REAL_IP']
    elif 'HTTP_X_FORWARDED_FOR' in request.environ:
        return request.environ['HTTP_X_FORWARDED_FOR'].split(',', -1)[0]
    elif 'HTTP_CLIENTIP' in request.environ:
        return request.environ['HTTP_CLIENTIP'].split(',', -1)[0]
    else:
        return request.remote_addr


LOG = config.getlogger()
syncpoint = Blueprint('sync', __name__, template_folder='syncfile')

# current_app to read config


def load_file_by_stream(filename, conf_dict):
    with open(filename, 'rb') as f:
        for chunk in iter(lambda: f.read(1024 * 1024), ''):
            yield chunk


def get_file_mtime(path, filename, conf_dict):
    version = conf_dict.get('version', 'default')
    if version != 'default':
        filename = filename + '.' + version
    template_file = os.path.join(syncpoint.root_path,
                                 syncpoint.template_folder, path, filename)
    fileStats = os.stat(template_file)
    return fileStats[stat.ST_MTIME]


def get_file_info(path, filename, conf_dict, md5only=True):
    version = conf_dict.get('version', 'default')
    if version != 'default':
        filename = filename + '.' + version
    if path == 'bin':
        binary_file = os.path.join(syncpoint.root_path,
                                   syncpoint.template_folder, path, filename)
        if md5only:
            m = hashlib.md5()
            for chunk in load_file_by_stream(binary_file, conf_dict):
                m.update(chunk)
            return m.hexdigest()
        else:
            response = Response(
                load_file_by_stream(binary_file, conf_dict),
                mimetype='application/octet-stream')  #bin application/octet-stream
            return response
    else:
        result = render_template(path + '/' + filename, **conf_dict)
        if md5only:
            return hashlib.new("md5", result).hexdigest()
        elif type(result) == types.UnicodeType:
            result = result.encode("utf-8")
            return result


@syncpoint.route('/')
@syncpoint.route('/<path:path>/')
@syncpoint.route('/<path:path>/<filename>')
def get_sync_file(path=None, filename=None):
    try:
        _ip = get_real_addr()
        info = {}
        query_result = db_session.query(Item)
        if path is not None:
            query_result = query_result.filter(Item.path.like(path + '%'))
        if filename is not None:
            query_result = query_result.filter(Item.filename == filename)
        exact_query_result = query_result.filter(Item.ip == _ip)
        if exact_query_result.count() > 0:  #if match,use ip
            query_result = exact_query_result
        for item in query_result.order_by(Item.uid).all():
            if re.compile(item.ip).match(_ip):
                conf_dict = {i.propertyname: i.value for i in item.config}
                mtime = get_file_mtime(item.path, item.filename, conf_dict)
                mtimes = [m.mtime for m in item.config if m.mtime is not None]
                if len(mtimes) > 0 and mtime < max(mtimes):
                    mtime = max(mtimes)
                if (item.path, item.filename) not in info:
                    info[(item.path, item.filename)] = (conf_dict, mtime)
                else:
                    d, m = info[(item.path, item.filename)]
                    d1 = dict(d, **conf_dict)
                    m1 = m if m > mtime else mtime
                    info[(item.path, item.filename)] = (d1, m1)
        param = request.args.get('param')
        if param:
            return json.dumps({str(k): v for k, v in info.items()})
        elif len(info) == 1 and filename and path:
            d, m = info[(path, filename)]
            if request.args.get('md5only'):
                return get_file_info(path, filename, d, True)
            return get_file_info(path, filename, d, False)
        else:
            ret = {}
            for k, v in info.items():
                path, fn = k
                conf_dict, mtime = v
                info = get_file_info(path, fn, conf_dict)
                ret[path + '/' + fn] = dict({'md5': info, 'mtime': mtime})
            if len(ret) > 0:
                return json.dumps(ret)
            else:
                abort(404)
    finally:
        db_session.close()
